/*******************************************************************
 *   > File Name: lcd.c
 *   > Author: fly
 *   > Mail: XXXXXXXX@icode.com
 *   > Create Time: 2020年05月11日 星期一 13时08分00秒
 ******************************************************************/
#include "include_all.h"

#define GPF0CON         (*(volatile unsigned long *)0xE0200120)
#define GPF1CON         (*(volatile unsigned long *)0xE0200140)
#define GPF2CON         (*(volatile unsigned long *)0xE0200160)
#define GPF3CON         (*(volatile unsigned long *)0xE0200180)

#define GPD0CON         (*(volatile unsigned long *)0xE02000A0)
#define GPD0DAT         (*(volatile unsigned long *)0xE02000A4)

#define CLK_SRC1        (*(volatile unsigned long *)0xe0100204)
#define CLK_DIV1        (*(volatile unsigned long *)0xe0100304)
#define DISPLAY_CONTROL (*(volatile unsigned long *)0xe0107008)

#define VIDCON0         (*(volatile unsigned long *)0xF8000000)
#define VIDCON1         (*(volatile unsigned long *)0xF8000004)
#define VIDTCON2        (*(volatile unsigned long *)0xF8000018)
#define WINCON0         (*(volatile unsigned long *)0xF8000020)
#define WINCON2         (*(volatile unsigned long *)0xF8000028)
#define SHADOWCON       (*(volatile unsigned long *)0xF8000034)
#define VIDOSD0A        (*(volatile unsigned long *)0xF8000040)
#define VIDOSD0B        (*(volatile unsigned long *)0xF8000044)
#define VIDOSD0C        (*(volatile unsigned long *)0xF8000048)

#define VIDW00ADD0B0    (*(volatile unsigned long *)0xF80000A0)
#define VIDW00ADD1B0    (*(volatile unsigned long *)0xF80000D0)

#define VIDTCON0        (*(volatile unsigned long *)0xF8000010)
#define VIDTCON1        (*(volatile unsigned long *)0xF8000014)

#define HSPW 			(40)				// 1~40 DCLK
#define HBPD			(10 - 1)			// 46
#define HFPD 			(240 - 1)			// 16 210 354
#define VSPW			(20)				// 1~20 DCLK
#define VBPD 			(10 - 1)			// 23
#define VFPD 			(30 - 1)			// 7 22 147

// FB地址
#define FB_ADDR			(0x23000000)
#define ROW				600//(480)
#define COL				1024//(800)
#define HOZVAL			(COL-1)
#define LINEVAL			(ROW-1)

#define XSIZE			COL
#define YSIZE			ROW

u32 *pfb = (u32 *)FB_ADDR;

// 常用颜色定义
#define RED             (0xFF0000)
#define GREEN           (0x00FF00)
#define BLUE            (0x0000FF)
#define WHITE           (0xFFFFFF)

// 初始化LCD
static void lcd_init(void)
{
    // 配置引脚用于LCD功能
    // GPF0CON:
    // bit[3:0]：	0010 = LCD_HSYNC
    // bit[7:4]: 	0010 = LCD_VSYNC
    // bit[11:8]: 	0010 = LCD_VDEN
    // bit[15:12]: 	0010 = LCD_VCLK
    // bit[19:16]: 	0010 = LCD_VD[0]
    // bit[23:20]: 	0010 = LCD_VD[1]
    // bit[27:24]: 	0010 = LCD_VD[2]
	// bit[31:28]: 	0010 = LCD_VD[3]
	// GPF1CON
	// bit[3:0]：	0010 = LCD_VD[4]
    // bit[7:4]: 	0010 = LCD_VD[5]
    // bit[11:8]: 	0010 = LCD_VD[6]
    // bit[15:12]: 	0010 = LCD_VD[7]
    // bit[19:16]: 	0010 = LCD_VD[8]
    // bit[23:20]: 	0010 = LCD_VD[9]
    // bit[27:24]: 	0010 = LCD_VD[10]
	// bit[31:28]:  0010 = LCD_VD[11]
	// GPF2CON
	// bit[3:0]：	0010 = LCD_VD[12]
    // bit[7:4]: 	0010 = LCD_VD[13]
    // bit[11:8]: 	0010 = LCD_VD[14]
    // bit[15:12]: 	0010 = LCD_VD[15]
    // bit[19:16]: 	0010 = LCD_VD[16]
    // bit[23:20]: 	0010 = LCD_VD[17]
    // bit[27:24]: 	0010 = LCD_VD[18]
	// bit[31:28]:  0010 = LCD_VD[19]
	// GPF3CON
	// bit[3:0]：	0010 = LCD_VD[20]
    // bit[7:4]: 	0010 = LCD_VD[21]
    // bit[11:8]: 	0010 = LCD_VD[22]
    // bit[15:12]: 	0010 = LCD_VD[23]
    // bit[19:16]: 	0010 = Reserved
    // bit[23:20]: 	0010 = Reserved
	
	GPF0CON = 0x22222222;
	GPF1CON = 0x22222222;
	GPF2CON = 0x22222222;
	GPF3CON = 0x22222222;
	
	// 打开背光PWMTOUT0(GPD0_0)
	GPD0CON &= ~(0xf<<0);	//clear bit[3:0]
	GPD0CON |= 0x1;			//output mode
	GPD0DAT &= ~(1<<0);		// output 0 to enable backlight
	
	// Display path selection,10: RGB=FIMD I80=FIMD ITU=FIMD
	DISPLAY_CONTROL |= 0x2;
	
	// VIDCON0
	// bit[28:26]:100 = WB interface and RGB interface,使用RGB接口
	// bit[18]: 0 = RGB parallel format,RGB 并行
	// bit[2]:0 = HCLK,选择时钟源为HCLK_DSYS=166MHz
	VIDCON0 &= ~( (3<<26)|(1<<18)|(1<<2) );
	
	// bit[1]:1 = Enables the video output and display control signal,使能lcd控制器
	// bit[0]:1 = Enables the video output and display control signal,当前帧结束后使能lcd控制器
	// * If this bit is set to “on” and “off”, then “H” is read and video
	// controller is enabled until the end of current frame
	VIDCON0 |= ( (1<<0)|(1<<1) );
	
	// bit[4]:选择需要分频
	// Selects the clock source as direct or divide using CLKVAL_F register.
	// 0 = Direct clock (frequency of VCLK = frequency of Clock source)
	// 1 = Divided by CLKVAL_F
	// bit[6~13]:分频系数为5，即VCLK = 166M/(4+1) = 33M
	// Determines the rates of VCLK and CLKVAL[7:0]. 0
	// VCLK = HCLK / (CLKVAL+1), where CLKVAL >= 1 Notes.
	// 1. The maximum frequency of VCLK is 100Mhz(pad:50pf).
	// 2. CLKSEL_F register selects Video Clock Source
	VIDCON0 |= 4<<6 | 1<<4;
	
	// H43-HSD043I9W1.pdf(p13) 时序图：VSYNC和HSYNC都是低脉冲
	// s5pv210芯片手册(p1207) 时序图：VSYNC和HSYNC都是高脉冲有效，所以需要反转
	VIDCON1 |= 1<<5 | 1<<6;
	
	// 设置时序
	VIDTCON0 = VBPD<<16 | VFPD<<8 | VSPW<<0;
	VIDTCON1 = HBPD<<16 | HFPD<<8 | HSPW<<0;
	// 设置长宽(物理屏幕)
	VIDTCON2 = (LINEVAL << 11) | (HOZVAL << 0);
	
	// 设置window0
	// bit[0]:使能
	// bit[2~5]:24bpp（RGB888）
	WINCON0 |= 1<<0;
	WINCON0 &= ~(0xf << 2);
	WINCON0 |= (0xB<<2) | (1<<15);
	
	#define LeftTopX     0
	#define LeftTopY     0
	#define RightBotX   1023//799
	#define RightBotY   599//479
	
	// 设置window0的上下左右
	// 设置的是显存空间的大小
	VIDOSD0A = (LeftTopX<<11) | (LeftTopY << 0);
	VIDOSD0B = (RightBotX<<11) | (RightBotY << 0);
	VIDOSD0C = (LINEVAL + 1) * (HOZVAL + 1);
	
	// 设置fb的地址
	VIDW00ADD0B0 = FB_ADDR;
	VIDW00ADD1B0 = (((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff);

	// 使能channel 0传输数据
	SHADOWCON = 0x1;
}

// 在像素点(x, y)处填充为color颜色
static inline void lcd_draw_pixel(u32 x, u32 y, u32 color)
{
	*(pfb + COL * y + x) = color;
}

// 把整个屏幕全部填充成一个颜色color
static void lcd_draw_background(u32 color)
{
	u32 i, j;
	
	for (j=0; j<ROW; j++)
	{
		for (i=0; i<COL; i++)
		{
			lcd_draw_pixel(i, j, color);
		}
	}
}

static void delay(void)
{
	volatile u32 i, j;
	for (i=0; i<4000; i++)
		for (j=0; j<1000; j++);
}

// 绘制横线，起始坐标为(x1, y)到(x2, y),颜色是color
static void lcd_draw_hline(u32 x1, u32 x2, u32 y,u32 color)
{
    u32 x;

    for(x = x1; x< x2; x++)
    {
        lcd_draw_pixel(x, y, color);
    }
}

// 绘制竖线，起始坐标为(x, y1)到(x, y2),颜色是color
static void lcd_draw_vline(u32 x, u32 y1, u32 y2, u32 color)
{
    u32 y;

    for(y = y1; y< y2; y++)
    {
        lcd_draw_pixel(x, y, color);
    }
}

void lcd_test(void)
{
	lcd_init();
	
	// 测试绘制背景色，成功
	while (1)
	{
		lcd_draw_background(RED);
		delay();
		
		lcd_draw_background(GREEN);
		delay();
		
		lcd_draw_background(BLUE);
		delay();
	}
}
